package com.gitee.l0km.xthrift.base.metadata;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import com.facebook.swift.codec.metadata.ThriftEnumMetadata;
import com.facebook.swift.codec.metadata.ThriftFieldMetadata;
import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.gitee.l0km.com4j.basex.doc.ClassCommentProvider;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;

import static com.gitee.l0km.xthrift.base.doc.CommentProviderFactories.providerOrNullOf;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * {@link ThriftEnumMetadata}的代理类<br>
 * 重载所有{@link ThriftFieldMetadata}相关方法
 * @author guyadong
 *
 */
@Immutable
public class DecoratorThriftEnumMetadata<T extends Enum<T>> extends ThriftEnumMetadata<T> {
	/**
	 * 类型名称转换实例<br>
	 * 输入原始类型名<br>
	 * 输出转换后的类型名
	 */
	public static Function<String, String> enumNameTransformer = null;
	public static Map<Class<? extends Enum<?>>,List<ExtensiveEnumItem>> beforeExtensiveEnumItems;
	public static Map<Class<? extends Enum<?>>,List<ExtensiveEnumItem>> afterExtensiveEnumItems;
    /**
	 * 根据传入的 {@link ThriftEnumMetadata} 对象创建对应的 {@link DecoratorThriftEnumMetadata} 对象
	 * <p>该方法会获取扩展枚举项信息，包括前置和后置的扩展项，然后创建并初始化一个新的代理对象</p>
	 * <br>
	 * @param key 原始的 {@link ThriftEnumMetadata} 对象，不能为 null
	 * @return 对应的 {@link DecoratorThriftEnumMetadata} 对象
	 */
    @SuppressWarnings({ "rawtypes", "unchecked" })
	@AoWeakCacheable
	private static DecoratorThriftEnumMetadata decoratorOf(ThriftEnumMetadata key){
		Map<Class<? extends Enum<?>> ,List<ExtensiveEnumItem>> before = (Map<Class<? extends Enum<?>> , List<ExtensiveEnumItem>>) firstNonNull(beforeExtensiveEnumItems, Collections.emptyMap());
		Map<Class<? extends Enum<?>> ,List<ExtensiveEnumItem>> after = (Map<Class<? extends Enum<?>> , List<ExtensiveEnumItem>>) firstNonNull(afterExtensiveEnumItems, Collections.emptyMap());
		return new DecoratorThriftEnumMetadata<>(key)
				.setExtensiveItemsBefore(before.get(key.getEnumClass()))
				.setExtensiveItemAfter(after.get(key.getEnumClass()));
	}
    /**  将{@link ThriftEnumMetadata}转换为 {@link DecoratorThriftEnumMetadata}对象 */
    @SuppressWarnings("rawtypes")
	public static final Function<ThriftEnumMetadata,ThriftEnumMetadata> 
    	ENUM_TRANSFORMER = new Function<ThriftEnumMetadata,ThriftEnumMetadata>(){
			@Override
			public ThriftEnumMetadata apply(@Nullable ThriftEnumMetadata input) {
				return null == input || input instanceof DecoratorThriftEnumMetadata
						? input
						: decoratorOf(input);
			}};
	private final ClassCommentProvider javadocCommentProvider ;
	private volatile AtomicReference<ImmutableList<String>> docs;
	private List<ExtensiveEnumItem> extensiveItemsBefore = Lists.newArrayList();
	private List<ExtensiveEnumItem> extensiveItemsAfter =  Lists.newArrayList();
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private DecoratorThriftEnumMetadata(ThriftEnumMetadata input){
		super(input.getEnumName(),input.getEnumClass());
		javadocCommentProvider = providerOrNullOf(getEnumClass());
	}
	@Override
	public ImmutableList<String> getDocumentation() {
		// double checking
		if(docs == null){
			synchronized (this) {
				if(docs == null){
					ImmutableList<String> _docs = super.getDocumentation();
					if( javadocCommentProvider != null){
						if(_docs == null || _docs.isEmpty()){
							_docs = ImmutableList.copyOf( javadocCommentProvider.commentOfClass());
						}
					}
					docs = new AtomicReference<>(_docs);
				}
			}
		}
		return docs.get();
	}

	@Override
	public Map<T, ImmutableList<String>> getElementsDocumentation() {
		Map<T, ImmutableList<String>> m = super.getElementsDocumentation();
		if(javadocCommentProvider != null){
	        ImmutableMap.Builder<T, ImmutableList<String>> elementDocs = ImmutableMap.builder();

			for (Entry<T, ImmutableList<String>> entry : m.entrySet()) {
				ImmutableList<String> value = entry.getValue();
				if(value.isEmpty()){
					value = ImmutableList.copyOf(javadocCommentProvider.commentOfField(entry.getKey().name()));
				}
				elementDocs.put(entry.getKey(), value);
			}
			return elementDocs.build();
		}
		return m;
	}
	@Override
	public String getEnumName() {
		return enumNameTransformer != null 
				? enumNameTransformer.apply(super.getEnumName()) 
				: super.getEnumName();
	}

	/**
	 * @return extensiveItemBefore
	 */
	public List<ExtensiveEnumItem> getExtensiveItemsBefore() {
		return extensiveItemsBefore;
	}
	/**
	 * @param extensiveItemBefore 要设置的 extensiveItemBefore
	 * @return 当前对象
	 */
	public DecoratorThriftEnumMetadata<T> setExtensiveItemsBefore(List<ExtensiveEnumItem> extensiveItemBefore) {
		if(extensiveItemBefore != null){
			this.extensiveItemsBefore.addAll(extensiveItemBefore);
			Collections.sort(this.extensiveItemsBefore);
			if( getByEnumConstant() != null){
				Integer first = getByEnumConstant().entrySet().iterator().next().getValue();
				List<ExtensiveEnumItem> reversed = Lists.reverse(this.extensiveItemsBefore);
				int last = first - 1;
				for(ExtensiveEnumItem ee:reversed){
					if(ee.constant == null){
						ee.constant = last --;
					}else{
						last = ee.constant - 1;
					}
				}
			}
		}
		return this;
	}
	/**
	 * @return extensiveItemAfter
	 */
	public List<ExtensiveEnumItem> getExtensiveItemsAfter() {
		return extensiveItemsAfter;
	}
	/**
	 * @param extensiveItemAfter 要设置的 extensiveItemAfter
	 * @return 当前对象
	 */
	public DecoratorThriftEnumMetadata<T> setExtensiveItemAfter(List<ExtensiveEnumItem> extensiveItemAfter) {
		if(extensiveItemAfter != null){
			this.extensiveItemsAfter = extensiveItemAfter;
		}
		return this;
	}


	public static class ExtensiveEnumItem implements Comparable<ExtensiveEnumItem>{
		public final String name;
		private Integer constant;
		public final ImmutableList<String> documentation;
		public ExtensiveEnumItem(String name, Integer constant, ImmutableList<String> documentation) {
			super();
			this.name = checkNotNull(name,"name is null");
			this.constant = constant;
			this.documentation = firstNonNull(documentation,ImmutableList.<String>of());
		}
		/**
		 * @return constant
		 */
		public Integer getConstant() {
			return constant;
		}
		/**
		 * @return name
		 */
		public String getName() {
			return name;
		}
		@Override
		public int compareTo(ExtensiveEnumItem o) {
			return name.compareTo(o.name);
		}
	}
}
